package com.wx.mall.common.utils;

import com.wx.mall.common.exception.BusinessException;
import com.wx.mall.common.exception.code.BrandResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.File;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;

/**
 * 上传文件工具类
 *
 * @author ljl
 */
@Slf4j
@Component
public class UploadUtil implements WebMvcConfigurer {
    public enum Encode {
        MD5,
        UUID
    }

//    public final String ENCODE_MD5 = "MD5";
//    public final String ENCODE_UUID = "UUID";

    // 上传返回结果集
    private Map<String, String> result;
    // 文件保存路径
    private String path = "";
//    private String fileName = "";
    /**
     * 以自定义文件命 命名
     *
     * @param file      上传文件
     * @param path      文件保存路径
     * @param name      文件保存的名字
     * @param webPrefix 文件网络前缀
     * @return
     */
    public Map<String, String> upload(MultipartFile file, String path, String name, String webPrefix) {
        this.path = path;
        String fileName = getFileName(file, name);
        handleUpload(file, fileName, webPrefix);
        return result;
    }


    public Map<String, String> upload(MultipartFile file, String path, String name) {
        this.path = path;
        handleUpload(file, getFileName(file, name));
        return result;
    }

    /**
     * 以UUID version 3, MD5 hash & namespace方式加密文件名
     *
     * @param file      上传文件
     * @param path      文件保存路径
     * @param name      文件保存的名字
     * @param webPrefix 文件网络前缀
     * @return
     */
    public Map<String, String> uploadUUID(MultipartFile file, String path, String name, String webPrefix) {
        this.path = path;
        // 对于相同的命名空间总是生成相同的UUID,版本为3,变体为9
        name = encodeFileName(getFileName(file, name),Encode.UUID);
        handleUpload(file, name, webPrefix);
        return result;
    }

    public Map<String, String> uploadUUID(MultipartFile file, String path, String name) {
        this.path = path;
        name = encodeFileName(getFileName(file, name),Encode.UUID);
        handleUpload(file, name);
        return result;
    }

    /**
     * 以MD5方式加密文件名
     *
     * @param file      上传文件
     * @param path      文件保存路径
     * @param name      文件保存的名字
     * @param webPrefix 文件网络前缀
     * @return
     */
    public Map<String, String> uploadMD5(MultipartFile file, String path, String name, String webPrefix) {
        this.path = path;
        name = encodeFileName(getFileName(file, name),Encode.MD5);
        handleUpload(file, name, webPrefix);
        return result;
    }

    public Map<String, String> uploadMD5(MultipartFile file, String path, String name) {
        this.path = path;
        name = encodeFileName(getFileName(file, name),Encode.MD5);
        handleUpload(file, name);
        return result;
    }

    /**
     * 以当前时间方式加密文件名(文件会重复)
     *
     * @param file      上传文件
     * @param path      文件保存路径
     * @param webPrefix 文件网络前缀
     * @return
     */
    public Map<String, String> uploadNowTime(MultipartFile file, String path, String webPrefix) {
        this.path = path;
        //获取当前时间
        String name = getNewFileName(file.getOriginalFilename(),String.valueOf(System.currentTimeMillis()));
        handleUpload(file, name, webPrefix);
        return result;
    }


    public Map<String, String> uploadNowTime(MultipartFile file, String path) {
        this.path = path;
        //获取当前时间
        String name = getNewFileName(file.getOriginalFilename(),String.valueOf(System.currentTimeMillis()));
        handleUpload(file, name);
        return result;
    }

    /**
     * 删除目录下所有文件 一级 即只存在文件
     * @param path
     */
    public Boolean remove(String path){
        this.path = new File(path).getAbsolutePath();
        File file = new File(this.path);
        removeFile(file);
        return !file.exists() || file.listFiles().length == 0;
    }

    /**
     * 删除单个文件
     * @param path
     * @param encryptedName 文件名称
     * @return
     */
    public Boolean remove(String path,String encryptedName){
        this.path = new File(path).getAbsolutePath();
        File file;
        if (path.endsWith("/")){
            file = new File(this.path + encryptedName);
        }else {
            file = new File(this.path + "/" + encryptedName);
        }
        removeFile(file);
        return !file.exists() || file.listFiles().length == 0;
    }

    /**
     * 删除单个文件
     * @param path
     * @param fileName 文件名称
     * @param encode 加密方式（MD5 UUID 不加密）
     * @return
     */
    public Boolean remove(String path,String fileName,Encode encode){
        return remove(path,encodeFileName(fileName,encode));
    }

    /**
     * MD5算法
     *
     * @param plainText
     * @return
     */
    private String stringToMD5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个md5算法！");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }

    private void handleUpload(MultipartFile file, String fileName, String webPrefix) {
        saveFile(file, fileName);

        result = new HashMap<>();
        result.put("name", file.getOriginalFilename());
        result.put("size", String.valueOf(file.getSize()));
        result.put("url", webPrefix + "/upload/" + fileName);
    }

    private void handleUpload(MultipartFile file, String fileName) {
        saveFile(file, fileName);

        result = new HashMap<>();
        result.put("name", file.getOriginalFilename());
        result.put("size", String.valueOf(file.getSize()));
        result.put("url", "/upload/" + fileName);
    }

    private String getFileName(MultipartFile file,String name) {
        String tempStr = name.toLowerCase();
        // 是否是图片格式名称
        boolean matcher = Pattern.compile("(.*)(\\.jpg|\\.png|\\.jpeg)$").matcher(tempStr).matches();
        String fileName;
        if (!matcher){
            fileName = getNewFileName(file.getOriginalFilename(), name);
        }else {
            fileName = name;
        }
        return fileName;
    }

    private String getNewFileName(String fileName, String toName) {
        // 获取文件的后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //文件重命名，实际处理肯定是要加上一段唯一的字符串（如现在时间），这里简单加 cun
//        String newFileName = name + "_logo" + suffixName;
        String newFileName = toName + suffixName;

        return newFileName;
    }

    private String encodeFileName(String fileName, Encode type) {
        // 获取文件名
        String name = fileName.substring(0,fileName.lastIndexOf("."));
        // 获取文件的后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        String newFileName;
        if ("MD5".equals(type.name())){
            // MD5加密
            newFileName = stringToMD5(name)+suffixName;
        }else if ("UUID".equals(type.name())){
            // 对于相同的命名空间总是生成相同的UUID,版本为3,变体为9
            newFileName = UUID.nameUUIDFromBytes(fileName.getBytes()).toString()+suffixName;
        }else {// 不加密
            newFileName =fileName;
        }

        return newFileName;
    }

    private void saveFile(MultipartFile file, String fileName) {
        if (file.isEmpty()) {
            throw new BusinessException(BrandResponseCode.UPLOAD_ERROR);
//            throw new RuntimeException("上传文件为空");
        }
        // 上传文件路径
        File uploadFile = new File(path);
        if (!uploadFile.exists()) {
            uploadFile.mkdirs();
        }


//        String newFile = path + "/" + getNewFileName(fileName, name);
        // 当path为相对路径时 执行file.transferTo(server_file)将报错（java.io.IOException: java.io.FileNotFoundException）
        // 所以需要将相对路径path转成绝对路径
//        String newFile = new File(path).getAbsolutePath() + "/" + getNewFileName(fileName, name);// E:\wxmall\mall\images/ff171ef354459a0776276d397fcbdd2b.jpg
        String newFile;
        if (path.endsWith("/")){

            newFile = new File(path + fileName).getAbsolutePath();
        }else {
            newFile = new File(path + "/" + fileName).getAbsolutePath();
        }
        try {
            File server_file = new File(newFile);
            file.transferTo(server_file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void removeFile (File dir){
        if (dir.isDirectory()){
            File[] files = dir.listFiles();
            for (File file :files ) {
                removeFile(file);
            }
        }else {
            dir.delete();
        }
    }
}
